﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Json
{
    internal class JsonDataDictionary : IOrderedDictionary
    {
        private readonly IDictionary<string, JsonData> _instanceDictionary = new Dictionary<string, JsonData>();
        private readonly IList<KeyValuePair<string, JsonData>> _dictionaryList = new List<KeyValuePair<string, JsonData>>();

        private ICollection EnsureCollection()
        {
            return (ICollection)_instanceDictionary;
        }

        private IDictionary EnsureDictionary()
        {
            return (IDictionary)_instanceDictionary;
        }


        #region IEnumerable Methods

        IEnumerator IEnumerable.GetEnumerator()
        {
            return EnsureCollection().GetEnumerator();
        }

        #endregion

        #region ICollection Properties

        int ICollection.Count
        {
            get { return EnsureCollection().Count; }
        }

        bool ICollection.IsSynchronized
        {
            get { return EnsureCollection().IsSynchronized; }
        }

        object ICollection.SyncRoot
        {
            get { return EnsureCollection().SyncRoot; }
        }

        #endregion

        #region ICollection Methods

        void ICollection.CopyTo(Array array, int index)
        {
            EnsureCollection().CopyTo(array, index);
        }

        #endregion

        #region IDictionary Properties

        bool IDictionary.IsFixedSize
        {
            get { return EnsureDictionary().IsFixedSize; }
        }

        bool IDictionary.IsReadOnly
        {
            get { return EnsureDictionary().IsReadOnly; }
        }

        ICollection IDictionary.Keys
        {
            get
            {
                IList<string> keys = _dictionaryList.Select(entry => entry.Key).ToList();

                return (ICollection)keys;
            }
        }

        ICollection IDictionary.Values
        {
            get
            {
                IList<JsonData> values = _dictionaryList.Select(entry => entry.Value).ToList();

                return (ICollection)values;
            }
        }

        #endregion

        #region IDictionary Methods

        void IDictionary.Add(object key, object value)
        {
            JsonData data = JsonData.ToJsonData(value);

            EnsureDictionary().Add(key, data);

            var entry =
                new KeyValuePair<string, JsonData>((string)key, data);
            _dictionaryList.Add(entry);

        }

        void IDictionary.Clear()
        {
            EnsureDictionary().Clear();
            _dictionaryList.Clear();
        }

        bool IDictionary.Contains(object key)
        {
            return EnsureDictionary().Contains(key);
        }

        IDictionaryEnumerator IDictionary.GetEnumerator()
        {
            return new OrderedDictionaryEnumerator(
                _dictionaryList.GetEnumerator());
        }

        void IDictionary.Remove(object key)
        {
            EnsureDictionary().Remove(key);

            for (int i = 0; i < _dictionaryList.Count; i++)
            {
                if (_dictionaryList[i].Key == (string)key)
                {
                    _dictionaryList.RemoveAt(i);
                    break;
                }
            }
        }

        #endregion

        #region IOrderedDictionary Methods

        void IOrderedDictionary.Insert(int idx, object key, object value)
        {
            var property = (string)key;
            JsonData data = JsonData.ToJsonData(value);

            this[property] = data;

            var entry =
                new KeyValuePair<string, JsonData>(property, data);

            _dictionaryList.Insert(idx, entry);
        }

        void IOrderedDictionary.RemoveAt(int idx)
        {          
            _instanceDictionary.Remove(_dictionaryList[idx].Key);
            _dictionaryList.RemoveAt(idx);
        }

        #endregion

        public object this[int idx]
        {
            get
            {
                return _dictionaryList[idx].Value;
            }

            set
            {
                JsonData data = JsonData.ToJsonData(value);

                KeyValuePair<string, JsonData> oldEntry = _dictionaryList[idx];

                _instanceDictionary[oldEntry.Key] = data;

                var entry = new KeyValuePair<string, JsonData>(oldEntry.Key, data);

                _dictionaryList[idx] = entry;
            }
        }

        #region IDictionary Indexer

        object IDictionary.this[object key]
        {
            get { return EnsureDictionary()[key]; }

            set
            {
                if (!(key is String))
                    throw new ArgumentException(
                        "The key has to be a string");

                JsonData data = JsonData.ToJsonData(value);

                this[(string)key] = data;
            }
        }

        #endregion

        public JsonData this[string propertyName]
        {
            get
            {
                return _instanceDictionary[propertyName];
            }

            set
            {
                var entry =
                    new KeyValuePair<string, JsonData>(propertyName, value);

                if (_instanceDictionary.ContainsKey(propertyName))
                {
                    for (int i = 0; i < _dictionaryList.Count; i++)
                    {
                        if (_dictionaryList[i].Key == propertyName)
                        {
                            _dictionaryList[i] = entry;
                            break;
                        }
                    }
                }
                else
                    _dictionaryList.Add(entry);

                _instanceDictionary[propertyName] = value;

            }
        }
    }
}